/*
 * 描述符处理所需的函数
 *
 * src/interfaces/ecpg/preproc/descriptor.c
 *
 * 由于描述符可能是字符串常量或字符串变量
 * 如果我们期望一个常量，则需要检查一个常量
 */

#include "postgres_fe.h"

#include "preproc_extern.h"

/*
 * 赋值处理函数（描述符）
 */

static struct assignment *assignments;

void push_assignment(char *fc_var, enum ECPGdtype fc_value)
{
	struct assignment *fc_new = (struct assignment *) mm_alloc(sizeof(struct assignment));

	fc_new->next = assignments;
	fc_new->variable = mm_alloc(strlen(fc_var) + 1);
	strcpy(fc_new->variable, fc_var);
	fc_new->value = fc_value;
	assignments = fc_new;
}

static void fc_drop_assignments(void)
{
	while (assignments)
	{
		struct assignment *fc_old_head = assignments;

		assignments = fc_old_head->next;
		free(fc_old_head->variable);
		free(fc_old_head);
	}
}

static void fc_ECPGnumeric_lvalue(char *fc_name)
{
	const struct variable *fc_v = find_variable(fc_name);

	switch (fc_v->type->type)
	{
		case ECPGt_short:
		case ECPGt_int:
		case ECPGt_long:
		case ECPGt_long_long:
		case ECPGt_unsigned_short:
		case ECPGt_unsigned_int:
		case ECPGt_unsigned_long:
		case ECPGt_unsigned_long_long:
		case ECPGt_const:
			fputs(fc_name, base_yyout);
			break;
		default:
			mmerror(PARSE_ERROR, ET_ERROR, "variable \"%s\" must have a numeric type", fc_name);
			break;
	}
}

/*
 * 描述符名称查找
 */

static struct descriptor *descriptors;

void add_descriptor(char *fc_name, char *fc_connection)
{
	struct descriptor *fc_new;

	if (fc_name[0] != '"')
		return;

	fc_new = (struct descriptor *) mm_alloc(sizeof(struct descriptor));

	fc_new->next = descriptors;
	fc_new->name = mm_alloc(strlen(fc_name) + 1);
	strcpy(fc_new->name, fc_name);
	if (fc_connection)
	{
		fc_new->connection = mm_alloc(strlen(fc_connection) + 1);
		strcpy(fc_new->connection, fc_connection);
	}
	else
		fc_new->connection = fc_connection;
	descriptors = fc_new;
}

void drop_descriptor(char *fc_name, char *fc_connection)
{
	struct descriptor *fc_i;
	struct descriptor **fc_lastptr = &descriptors;

	if (fc_name[0] != '"')
		return;

	for (fc_i = descriptors; fc_i; fc_lastptr = &fc_i->next, fc_i = fc_i->next)
	{
		if (strcmp(fc_name, fc_i->name) == 0)
		{
			if ((!fc_connection && !fc_i->connection)
				|| (fc_connection && fc_i->connection
					&& strcmp(fc_connection, fc_i->connection) == 0))
			{
				*fc_lastptr = fc_i->next;
				if (fc_i->connection)
					free(fc_i->connection);
				free(fc_i->name);
				free(fc_i);
				return;
			}
		}
	}
	if (fc_connection)
		mmerror(PARSE_ERROR, ET_WARNING, "descriptor %s bound to connection %s does not exist", fc_name, fc_connection);
	else
		mmerror(PARSE_ERROR, ET_WARNING, "descriptor %s bound to the default connection does not exist", fc_name);
}

struct descriptor
		   *
lookup_descriptor(char *fc_name, char *fc_connection)
{
	struct descriptor *fc_i;

	if (fc_name[0] != '"')
		return NULL;

	for (fc_i = descriptors; fc_i; fc_i = fc_i->next)
	{
		if (strcmp(fc_name, fc_i->name) == 0)
		{
			if ((!fc_connection && !fc_i->connection)
				|| (fc_connection && fc_i->connection
					&& strcmp(fc_connection, fc_i->connection) == 0))
				return fc_i;
			if (fc_connection && !fc_i->connection)
			{
				/* 覆盖描述符的连接 */
				fc_i->connection = mm_strdup(fc_connection);
				return fc_i;
			}
		}
	}
	if (fc_connection)
		mmerror(PARSE_ERROR, ET_WARNING, "descriptor %s bound to connection %s does not exist", fc_name, fc_connection);
	else
		mmerror(PARSE_ERROR, ET_WARNING, "descriptor %s bound to the default connection does not exist", fc_name);
	return NULL;
}

void output_get_descr_header(char *fc_desc_name)
{
	struct assignment *fc_results;

	fprintf(base_yyout, "{ ECPGget_desc_header(__LINE__, %s, &(", fc_desc_name);
	for (fc_results = assignments; fc_results != NULL; fc_results = fc_results->next)
	{
		if (fc_results->value == ECPGd_count)
			fc_ECPGnumeric_lvalue(fc_results->variable);
		else
			mmerror(PARSE_ERROR, ET_WARNING, "descriptor header item \"%d\" does not exist", fc_results->value);
	}

	fc_drop_assignments();
	fprintf(base_yyout, "));\n");
	whenever_action(3);
}

void output_get_descr(char *fc_desc_name, char *fc_index)
{
	struct assignment *fc_results;

	fprintf(base_yyout, "{ ECPGget_desc(__LINE__, %s, %s,", fc_desc_name, fc_index);
	for (fc_results = assignments; fc_results != NULL; fc_results = fc_results->next)
	{
		const struct variable *fc_v = find_variable(fc_results->variable);
		char	   *fc_str_zero = mm_strdup("0");

		switch (fc_results->value)
		{
			case ECPGd_nullable:
				mmerror(PARSE_ERROR, ET_WARNING, "nullable is always 1");
				break;
			case ECPGd_key_member:
				mmerror(PARSE_ERROR, ET_WARNING, "key_member is always 0");
				break;
			default:
				break;
		}
		fprintf(base_yyout, "%s,", get_dtype(fc_results->value));
		ECPGdump_a_type(base_yyout, fc_v->name, fc_v->type, fc_v->brace_level,
						NULL, NULL, -1, NULL, NULL, fc_str_zero, NULL, NULL);
		free(fc_str_zero);
	}
	fc_drop_assignments();
	fputs("ECPGd_EODT);\n", base_yyout);

	whenever_action(2 | 1);
}

void output_set_descr_header(char *fc_desc_name)
{
	struct assignment *fc_results;

	fprintf(base_yyout, "{ ECPGset_desc_header(__LINE__, %s, (int)(", fc_desc_name);
	for (fc_results = assignments; fc_results != NULL; fc_results = fc_results->next)
	{
		if (fc_results->value == ECPGd_count)
			fc_ECPGnumeric_lvalue(fc_results->variable);
		else
			mmerror(PARSE_ERROR, ET_WARNING, "descriptor header item \"%d\" does not exist", fc_results->value);
	}

	fc_drop_assignments();
	fprintf(base_yyout, "));\n");
	whenever_action(3);
}

static const char * fc_descriptor_item_name(enum ECPGdtype fc_itemcode)
{
	switch (fc_itemcode)
	{
		case ECPGd_cardinality:
			return "CARDINALITY";
		case ECPGd_count:
			return "COUNT";
		case ECPGd_data:
			return "DATA";
		case ECPGd_di_code:
			return "DATETIME_INTERVAL_CODE";
		case ECPGd_di_precision:
			return "DATETIME_INTERVAL_PRECISION";
		case ECPGd_indicator:
			return "INDICATOR";
		case ECPGd_key_member:
			return "KEY_MEMBER";
		case ECPGd_length:
			return "LENGTH";
		case ECPGd_name:
			return "NAME";
		case ECPGd_nullable:
			return "NULLABLE";
		case ECPGd_octet:
			return "OCTET_LENGTH";
		case ECPGd_precision:
			return "PRECISION";
		case ECPGd_ret_length:
			return "RETURNED_LENGTH";
		case ECPGd_ret_octet:
			return "RETURNED_OCTET_LENGTH";
		case ECPGd_scale:
			return "SCALE";
		case ECPGd_type:
			return "TYPE";
		default:
			return NULL;
	}
}

void output_set_descr(char *fc_desc_name, char *fc_index)
{
	struct assignment *fc_results;

	fprintf(base_yyout, "{ ECPGset_desc(__LINE__, %s, %s,", fc_desc_name, fc_index);
	for (fc_results = assignments; fc_results != NULL; fc_results = fc_results->next)
	{
		const struct variable *fc_v = find_variable(fc_results->variable);

		switch (fc_results->value)
		{
			case ECPGd_cardinality:
			case ECPGd_di_code:
			case ECPGd_di_precision:
			case ECPGd_precision:
			case ECPGd_scale:
				mmfatal(PARSE_ERROR, "descriptor item \"%s\" is not implemented",
						fc_descriptor_item_name(fc_results->value));
				break;

			case ECPGd_key_member:
			case ECPGd_name:
			case ECPGd_nullable:
			case ECPGd_octet:
			case ECPGd_ret_length:
			case ECPGd_ret_octet:
				mmfatal(PARSE_ERROR, "descriptor item \"%s\" cannot be set",
						fc_descriptor_item_name(fc_results->value));
				break;

			case ECPGd_data:
			case ECPGd_indicator:
			case ECPGd_length:
			case ECPGd_type:
				{
					char	   *fc_str_zero = mm_strdup("0");

					fprintf(base_yyout, "%s,", get_dtype(fc_results->value));
					ECPGdump_a_type(base_yyout, fc_v->name, fc_v->type, fc_v->brace_level,
									NULL, NULL, -1, NULL, NULL, fc_str_zero, NULL, NULL);
					free(fc_str_zero);
				}
				break;

			default:
				;
		}
	}
	fc_drop_assignments();
	fputs("ECPGd_EODT);\n", base_yyout);

	whenever_action(2 | 1);
}

/* 我认为动态分配是多余的，因为每个语句最多可能有两个描述符
   变量。（输入和输出描述符）
   并且描述符不是普通变量，因此它们不属于
   变量列表。
*/

#define MAX_DESCRIPTOR_NAMELEN 128
struct variable * descriptor_variable(const char *fc_name, int fc_input)
{
	static char fc_descriptor_names[2][MAX_DESCRIPTOR_NAMELEN];
	static struct ECPGtype fc_descriptor_type = {ECPGt_descriptor, NULL, NULL, NULL, {NULL}, 0};
	static struct variable fc_varspace[2] = {
		{fc_descriptor_names[0], &fc_descriptor_type, 0, NULL},
		{fc_descriptor_names[1], &fc_descriptor_type, 0, NULL}
	};

	strlcpy(fc_descriptor_names[fc_input], fc_name, sizeof(fc_descriptor_names[fc_input]));
	return &fc_varspace[fc_input];
}

struct variable * sqlda_variable(const char *fc_name)
{
	struct variable *fc_p = (struct variable *) mm_alloc(sizeof(struct variable));

	fc_p->name = mm_strdup(fc_name);
	fc_p->type = (struct ECPGtype *) mm_alloc(sizeof(struct ECPGtype));
	fc_p->type->type = ECPGt_sqlda;
	fc_p->type->size = NULL;
	fc_p->type->struct_sizeof = NULL;
	fc_p->type->u.element = NULL;
	fc_p->type->counter = 0;
	fc_p->brace_level = 0;
	fc_p->next = NULL;

	return fc_p;
}
